/*
 *	CHANGE LOG
 * 
 * 01.02.01
 *
 *
 *		10/07/2008 - DW - Changed the aliases in GetTabIDByPortalID.  They were after the (nolock) hint,
 *						but this seemed to cause errors on some installs of SQL Server.
 * 
 * 01.02.02
 * 
 *		12/09/2008 - DW - Changed the permalink code to use the new permalink code in the 3.5 version 
 *							of the blog module.  Now we call BlogNavigateURL to get the SEO Friendly URL.
 * 
 * 01.02.03
 * 
 *		12/23/2008 - DW - Added authorization code to make sure calls are authorized.
 * 
 * 01.02.04
 * 
 *		02/20/2008 - DW - Updated call to BlogNavigateURL to account for changes in signature.
 * 
 * 01.02.05
 * 
 *      11/30/2009 - DW - Closed the data readers when connections are made to the db. 
 * 
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Web;
using DotNetNuke.Common;
using DotNetNuke.Data;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Users;
using DotNetNuke.Modules.Blog.Business;

namespace ITCrossing
{
	public class metaPostBlogModuleProvider : IPublishable, ILinkable
	{
		# region Public Properties

		/// <summary>
		/// ProviderKey should be set to the Friendly Name of the module for
		/// which this provider is being implemented.
		/// </summary>
		public string ProviderKey
		{
			get
			{
				return MetaPostServices.GetFriendlyNameFromModuleDefinition("View_Blog");
			}
		}

		/// <summary>
		/// ManifestPath is the path to the manifest file used to determine 
		/// the settings available in Windows Live Writer.  An example path might
		/// be /DesktopModules/itcMetaPost/manifests/wlwblog.xml.  See the following URL for 
		/// information regarding what Content can be placed in a WLW manifest file:
		/// http://msdn2.microsoft.com/en-us/library/bb463260.aspx
		/// </summary>
		public string ManifestFilePath
		{
			get { return "/DesktopModules/itcMetaPost/manifests/wlwblog.xml"; }
		}

		/// <summary>
		/// LocalizationFilePath is the path including the filename to the 
		/// resource file used by the module to retrieve the error messages
		/// shown to the user (eg. "/DesktopModules/custommodule/App_LocalResources/customfilename")
		/// Note that the filename should not include the file extension.
		/// </summary>
		public string LocalizationFilePath
		{
			get { return "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPostBlog"; }
		}

		/// <summary>
		/// OPTIONAL - ImageUploadPath is a property metaPost uses to determine where 
		/// to store the media files (images).  This property should return just a 
		/// section of the path starting from the portal root.  Added to this path will
		/// be the username along with the path 
		/// </summary>
		public string ImageUploadPath
		{
			get
			{
				// This can be overridden with custom implementation if needed.  If
				// overridden, then a message should be placed in the ImagePath column
				// for your provider letting administrators know that administration of
				// the ImagePath is implemented elsewhere.  
				return MetaPostServices.GetProviderImageUploadPath(ProviderKey);
			}
		}

		/// <summary>
		/// AttachmentUploadPath is used to specify the location of attachments added
		/// to a post or page.
		/// </summary>
		public string AttachmentUploadPath
		{
			get
			{
				// For now, we'll just set this to the same as the image upload path
				return MetaPostServices.GetProviderAttachmentUploadPath(ProviderKey);
			}
		}

		/// <summary>
		/// The FooterScriptIncludePath can be used to configure a custom location
		/// for the script which determines the widgets shown in the footer of the 
		/// post.  While FooterScriptIncludePath is not in the IPublishable interface,
		/// metaPost uses reflection to check for its existence.  If this property is 
		/// included on a provider, then this is a way to override the default setting:
		/// "/DesktopModules/itcMetaPost/js/mg.js"
		/// </summary>
		public string FooterScriptIncludePath
		{
			get { return "/DesktopModules/itcMetaPost/js/m.js";  }
		}

		/// <summary>
		/// SettingsUserControlPath is for future use.  It will return the path to the
		/// the settings user control for this provider.  
		/// </summary>
		public string SettingsUserControlPath
		{
			get { throw new NotImplementedException(); }
		}

		# endregion

		#region IPublishable Members

		# region Get Methods

		/// <summary>
		/// The purpose of this procedure is to provide a list of all the modules in a 
		/// given portal to which the currently logged on user has either insert, update
		/// or delete access.  Note, this may not always correspond to an actual module.
		/// For example, with the DNN Blog module provider, a list of blogs is returned
		/// along with URL of where each blog is available within the site.
		/// </summary>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		/// <param name="providerKey"></param>
		public ModuleInfoStruct[] GetModulesForUser(UserInfo userInfo, PortalSettings portalSettings, string providerKey)
		{
			ModuleInfoStruct[] infoArray = new ModuleInfoStruct[1];

			//Retrieve the BlogInfo object for the currently logged in user.
			BlogController blogController = new BlogController();
			BlogInfo objBlogInfo = blogController.GetBlogByUserName(userInfo.PortalID, userInfo.Username);

			// Get BlogUrl from 
			string blogURL;
			int blogTabId = -1;
			if (HttpContext.Current.Request["sid"] != null && HttpContext.Current.Request["sid"].ToString() != string.Empty)
			{
				blogTabId = Convert.ToInt32(HttpContext.Current.Request["sid"]);
			}
			else
			{
				blogTabId = GetTabIDByPortalID(portalSettings.PortalId.ToString());
			}

			if (objBlogInfo != null)
			{
				ModuleInfoStruct structBlogInfo = new ModuleInfoStruct();
				structBlogInfo.ModuleLevelId = objBlogInfo.BlogID.ToString();
				structBlogInfo.ModuleName = objBlogInfo.Title;

				structBlogInfo.Url = MetaPostServices.GetRedirectUrl(providerKey, blogTabId);
				infoArray[0] = structBlogInfo;
			}
			else
			{
				throw new MetaPostException("NoModulesForUser",
				                            "There is no blog associated with this user account.  Please enter a user account which has been associated with a blog.");
			}
			return infoArray;
		}

		/// <summary>
		/// GetRecentItems returns a list of the recent items added to the module.  For example, with
		/// the blog module, the list would return all the recent blog entries.  For the Text/HTML provider, 
		/// this method would return a list of the recently added Text/HTML modules in the site.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="numberOfItems"></param>
		/// <param name="requestType">GetRecentItems can be called for a number of reasons.  In Windows Live Writer,
		/// users are able to retrieve a list of recent posts or recent pages.  The requestType parameter is used
		/// to denote whether a list of pages or posts is being requested.  Additionally, WLW may request a list 
		/// of hiearchical pages to show pages when allowing a parent page to be assigned to a new page.  The values for this enum
		/// are RecentPosts, RecentPages and RecentPagesHiearchical.</param>
		/// <param name="providerKey"></param>
		/// <returns></returns>
		public Item[] GetRecentItems(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, int numberOfItems, RecentItemsRequestType requestType, string providerKey)
		{

			// Authorize user for call to this method
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
										   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			Item[] itemArray = null;
			BlogController objBlogController = new BlogController();
			int intBlogID =
				objBlogController.GetBlogByUserID(portalSettings.PortalId, userInfo.UserID).BlogID;
			ArrayList arEntries =
				new EntryController().ListEntriesByBlog(intBlogID, DateTime.Now.ToUniversalTime(), true, true,
														20);

			// Find which is the least, numberOfPosts or arEntries.Count
			int loopCutOff = (numberOfItems >= arEntries.Count) ? arEntries.Count : numberOfItems;

			itemArray = new Item[loopCutOff];

			int i = 0;
			foreach (EntryInfo entry in arEntries)
			{
				Item item = new Item();
				item.Link = entry.PermaLink;
				item.Content = HttpUtility.HtmlDecode(entry.Entry);
				item.Summary = entry.Description;
				item.DateCreated = entry.AddedDate.AddMinutes(GetTimeZoneOffset(Convert.ToInt32(moduleLevelId)));
				item.ItemId = entry.EntryID.ToString();
				item.Title = entry.Title;
				item.Permalink = entry.PermaLink;
				itemArray[i] = item;
				i++;
				if (i >= loopCutOff) break;
			}

			return itemArray;
		}

		/// <summary>
		/// GetItem is used to retrieve a specific item, which may be a page or a post if your module supports
		/// both concepts.  In cases where a module supports both pages and posts, a concept made available in 
		/// Windows Live Writer, but not in Word 2007 or in many other publishing clients, the itemType parameter 
		/// can be used to distinguish between when this method is being called to add a page or a post.  If your 
		/// provider doesn't support a distinction between pages and posts, then simply ignore the itemType parameter.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public Item GetItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			EntryController entryController = new EntryController();
			Item item = new Item();

			EntryInfo entryInfo = GetEntry(itemId, portalSettings.PortalId);
			string blogId = GetBlogIdFromEntry(entryInfo);
			
			// Authorize the user first, now that we have an entry and therefore have the blogID
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, blogId,
										   GetModulesForUser(userInfo, portalSettings, ProviderKey));			


			if (entryInfo == null)
				throw new MetaPostException("NoPostAvailable",
											"There was en error retrieving the blog entry.  Try closing and restarting the software you're using to edit your blog post.");
			item.Link = entryInfo.PermaLink;
			item.Content = HttpUtility.HtmlDecode(entryInfo.Entry);
			item.Summary = HttpUtility.HtmlDecode(entryInfo.Description);
			item.DateCreated = entryInfo.AddedDate.AddMinutes(GetTimeZoneOffset(entryInfo.BlogID));
			item.StartDate = entryInfo.AddedDate;
			item.ItemId = entryInfo.EntryID.ToString();
			item.Title = entryInfo.Title;
			item.Permalink = entryInfo.PermaLink;
			item.AllowComments = (entryInfo.AllowComments) ? 1 : 0;

			if (itemType == ItemType.Post)
				FindAndPlaceSummary(ref item);

			return item;
		}

		# endregion

		# region Add, Edit and Delete Methods


		/// <summary>
		/// NewItem is used for creating new items (either pages or posts).  The implementation 
		/// in your provider can distinguish between page and post requests for NewItem by the 
		/// ItemType property of the Item struct.  Additional properties of the Item Struct which 
		/// may be of interest in the implmentation of this method are: Publish, StyleDetectionPost,StyleId, and ItemType.
		/// See the comments associated with each of these properties of the Item struct for more information.
		/// </summary>
		/// <param name="moduleLevelId">String - ModuleId is a very loose concept here.  Anything can be tracked in this variable as long as it is consistent.  For the Blog module, the BlogId is passed and for the Text/HTML module, the ModuleId is passed and for the Ventrian News Articles module, the ArticleId is passed.  This value is based on the value sent to Windows Live Writer when GetModulesForUser is called. </param>
		/// <param name="userInfo">DotNetNuke UserInfo object.  This UserInfo object is retrieved by metaPost and made available to every method call in the MetaPostProvider interface.</param>
		/// <param name="portalSettings">DotNetNuke PortaSettings object - This is provided to make it easier to work with metaPost.  metaPost does all the work behind the scenes of identifying the right portalSettings object based on the requested URL.</param>
		/// <param name="item">Custom Struct - The item struct contains a list of fields sent through the MetaWeblog API.  The properties have been renamed to make sense in more general applications outside of just blogs.</param>
		/// <returns></returns>
		public string NewItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{
			// Authorize this attempt to create a new item
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
			                               GetModulesForUser(userInfo, portalSettings, ProviderKey));
			
			List<string> images = MetaPostServices.GetImageListFromItem(ref item);
			
			ExtractSummaryFromExtendedContent(ref item);

			int entryId = 0;
			BlogController blogController = new BlogController();
			EntryController objEntryController = new EntryController();
			EntryInfo objEntry = new EntryInfo();
			int blogTabID = 0;
			int tempBlogID = 0;
			objEntry.Title = item.Title;

			// HtmlEncode the entry.
			objEntry.Entry = HttpUtility.HtmlEncode(item.Content);

			// Retrieve the TabID where this blog can be viewed
			if (HttpContext.Current.Request["sid"] != null && HttpContext.Current.Request["sid"].ToString() != string.Empty)
			{
				blogTabID = Convert.ToInt32(HttpContext.Current.Request["sid"]);
			}
			else
			{
				blogTabID = GetTabIDByPortalID(portalSettings.PortalId.ToString());
			}


			// Check to see if the user selected a child blog (through the use of Categories)
			if (item.Categories != null && item.Categories.Length > 0)
			{
				// Check to make sure the user only entered 1 category.
				if (item.Categories.Length > 1)
					throw new MetaPostException("ChildBlogSelectionLength", "Please select only one child blog.");
				// Retrieve BlogID of the child blog if passed through the Categories
				ArrayList usersBlogs =
					blogController.ListBlogs(portalSettings.PortalId, Convert.ToInt32(moduleLevelId),
					                         true);
				foreach (BlogInfo bi in usersBlogs)
				{
					if (bi.Title == item.Categories[0])
					{
						tempBlogID = bi.BlogID;
					}
				}

				// If the user tried to select a Child blog, but we couldn't retrieve 
				// the BlogID, then let them know through an error.
				if (tempBlogID == 0)
					throw new MetaPostException("ChildBlogNotAvailable",
					                            "The child blog you selected is not available.");
			}
			// If we didn't get a child blogID, then we'll use the parent BlogID.
			if (tempBlogID == 0) tempBlogID = Convert.ToInt32(moduleLevelId);


			// Make sure the AddedDate is valid
			if (item.DateCreated.Year > 1)
			{
				// WLW manages the TZ offset automatically
				objEntry.AddedDate = item.DateCreated;
			}
			else
			{
				objEntry.AddedDate = DateTime.Now.ToUniversalTime();
			}

			objEntry.AllowComments = (item.AllowComments == -1 || item.AllowComments == 1) ? true : false;

			objEntry.Published = item.Publish;
			objEntry.Description = item.Summary;
			objEntry.DisplayCopyright = false;
			objEntry.Copyright = "";
			objEntry.BlogID = tempBlogID;
			entryId = objEntryController.AddEntry(objEntry);
			objEntry.EntryID = entryId;
			
			objEntry.PermaLink = SetEntryPermalink(blogTabID, portalSettings.PortalId, objEntry);
			
			objEntryController.UpdateEntry(objEntry);

			MetaPostServices.SaveItemImages(entryId, images, ProviderKey, portalSettings, userInfo.UserID);

			return entryId.ToString();
		}

		/// <summary>
		/// EditItem is called to edit either a page or a post.  The difference
		/// between the two is denoted in a property of the Item struct (ItemType).
		/// An additional property of the Item struct that may be important to the 
		/// implementation of the EditItem method is the Publish property.  This is 
		/// set to true when the item should be published.  The default setting of 
		/// false denotes that the item may be saved as a draft.
		/// 
		/// It's important to note that when Windows Live Writer sends the information 
		///		for a post edit, the moduleLevelId is not passed.  For implementation of 
		///		this procedure in providers which do not support pages, the moduleLevelId 
		///		should be ignored and attempts to get the moduleId can be made by calling
		///		GetModuleIdFromItemId or a similar private procedure.
		/// 
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool EditItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{


			List<string> images = MetaPostServices.GetImageListFromItem(ref item);
			int blogTabID;

			// Retrieve the TabID where this blog can be viewed
			if (HttpContext.Current.Request["sid"] != null && HttpContext.Current.Request["sid"].ToString() != string.Empty)
			{
				blogTabID = Convert.ToInt32(HttpContext.Current.Request["sid"]);
			}
			else
			{
				blogTabID = GetTabIDByPortalID(portalSettings.PortalId.ToString());
			}


			ExtractSummaryFromExtendedContent(ref item);

			EntryInfo objEntry = GetEntry((string)item.ItemId, portalSettings.PortalId);
			string blogId = GetBlogIdFromEntry(objEntry);
			// Now that we have the BlogID, we can authorize this edit
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, blogId,
										   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			objEntry.Title = item.Title;
			objEntry.Entry = item.Content;
			objEntry.AllowComments = (item.AllowComments == -1 || item.AllowComments == 1)? true : false;

			// HtmlEncode the entry
			objEntry.Entry = HttpUtility.HtmlEncode(objEntry.Entry);
			objEntry.Description = item.Summary;

			if (item.DateCreated.Year > 1)
			{
				// WLW handles TZ offset automatically.
				// objEntry.AddedDate = item.DateCreated;
				// It appears that the Blog module ignores changes to the publish date, 
				// so we'll update it ourselves
				string SQL = "UPDATE {databaseOwner}[{objectQualifier}Blog_Entries] SET AddedDate = '"
				             + item.DateCreated.ToString()
				             + "' WHERE EntryID = " + objEntry.EntryID;
				DataProvider.Instance().ExecuteSQL(SQL);

			}
			else
			{
				// Nothing happens since we want to keep the time previously entered.
			}
			objEntry.Published = item.Publish;
			objEntry.PermaLink = SetEntryPermalink(blogTabID, portalSettings.PortalId, objEntry);
			EntryController objEntryController = new EntryController();
			objEntryController.UpdateEntry(objEntry);

			MetaPostServices.SaveItemImages(objEntry.EntryID, images, ProviderKey, portalSettings, userInfo.UserID);

			return true;
		}

		/// <summary>
		/// DeleteItem is used to delete either a page or a post.  The difference between the two is noted 
		/// in the itemType parameter which is an enumeration set to either Page or Post.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public bool DeleteItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			EntryInfo objEntry = GetEntry(itemId, portalSettings.PortalId);
			string blogId = GetBlogIdFromEntry(objEntry);
			// Now that we have the BlogID, we can authorize this delete
			MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, blogId,
										   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			//Create new BlogController to delete the blog entry.
			EntryController objEntryController = new EntryController();
			objEntryController.DeleteEntry(Convert.ToInt32(itemId));
			return true;
		}

		# endregion

		# region Category Related Procedures

		/// <summary>
		/// GetCategories retrieves a list of categories for a particular module.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public ItemCategoryInfo[] GetCategories(string moduleLevelId, UserInfo userInfo,
		                                        PortalSettings portalSettings)
		{
			BlogController blogController = new BlogController();
			ItemCategoryInfo[] categories = null;

			//Retrieve the BlogInfo object for the currently logged in user.
			BlogInfo objBlogInfo = blogController.GetBlogByUserName(userInfo.PortalID, userInfo.Username);

			ArrayList usersBlogs =
				blogController.ListBlogs(portalSettings.PortalId, objBlogInfo.BlogID, true);


			// Get the URL for the blog - to be used later
			int blogTabID = GetTabIDByPortalID(portalSettings.PortalId.ToString());
			string blogURL = Globals.NavigateURL(blogTabID);

			if (usersBlogs != null && usersBlogs.Count > 0)
			{
				categories = new ItemCategoryInfo[usersBlogs.Count];
				for (int i = 0; i < usersBlogs.Count; i++)
				{
					BlogInfo bi = (BlogInfo) usersBlogs[i];
					ItemCategoryInfo category = new ItemCategoryInfo();

					//First Category
					category.CategoryId = bi.BlogID;
					category.HtmlUrl = blogURL;
					category.RssUrl = bi.SyndicationURL.ToString();
					category.Description = bi.Title;
					category.CategoryName = bi.Title;
					categories[i] = category;
				}
			}

			return categories;
		}

		public bool SetItemCategories(string itemId, ItemCategory[] itemCategories, UserInfo userInfo,
		                        PortalSettings portalSettings)
		{
			// Not yet implemented.
			return true;
		}

		public ItemCategory[] GetItemCategories(string itemId, UserInfo userInfo,
		                                        PortalSettings portalSettings)
		{
			// Not yet implemented.
			return null;
		}

		/// <summary>
		/// Used to add a new category for modules which support this feature.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public int NewCategory(string moduleLevelId, NewItemCategory category, UserInfo userInfo, PortalSettings portalSettings)
		{
			throw new MetaPostException("FeatureNotImplemented",
			                            "This feature is currently not implemented.");
		}

		# endregion

		# region ILinkable

		/// <summary>
		/// ModuleName is used by metaPost when sending Trackbacks and Pings.  If these are not used by your 
		/// module, then you don't need to implement this procedure.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public string GetModuleName(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings)
		{
			// First authorize this action
			// Only called during valid edit and new operation
			//MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
			//                   GetModulesForUser(userInfo, portalSettings, ProviderKey));			

			int blogId;

			# region Commented Code Left for Reference
			// Included when we were passing IDType
			//if (idType == IDType.Module)
			//{
			//    moduleLevelId = Convert.ToInt32(id);
			//}
			//else
			//{
			//    moduleLevelId = Convert.ToInt32(GetModuleIdFromItemId(id, portalSettings.PortalId));
			//}
			# endregion

			blogId = Convert.ToInt32(moduleLevelId);

			BlogController blogController = new BlogController();
			BlogInfo blogInfo = blogController.GetBlog(blogId);

			return blogInfo.Title;

		}

		/// <summary>
		/// ModuleName is used by metaPost when sending Trackbacks and Pings.  If these are not used by your 
		/// module, then you don't need to implement this procedure.
		/// </summary>
		/// <param name="id"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public string GetPermaLink(string id, string itemId, UserInfo userInfo, PortalSettings portalSettings)
		{

			int entryId;

			entryId = Convert.ToInt32(itemId);

			EntryInfo entryInfo = GetEntry(itemId, portalSettings.PortalId);

			string blogId = GetBlogIdFromEntry(entryInfo);

			// First authorize this action
			// Only called during valid edit and new operation
			//MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, blogId,
			//                   GetModulesForUser(userInfo, portalSettings, ProviderKey));			


			return entryInfo.PermaLink;

		}

		public TrackbackAndPingSettings GetPingbackSettings(string moduleLevelId, UserInfo userInfo,
		                                                            PortalSettings portalSettings)
		{

			// First authorize this action
			// Only called during valid edit and new operation
			//MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
			//                   GetModulesForUser(userInfo, portalSettings, ProviderKey));			


			TrackbackAndPingSettings trackbackSettings = new TrackbackAndPingSettings();

			trackbackSettings.AllowAutoDiscovery = true;
			trackbackSettings.AllowForPage = true;
			trackbackSettings.AllowForPost = true;
			trackbackSettings.AllowOverrideByPingDropDown = true;


			return trackbackSettings;
		}

		# endregion

		# region Optional Procedures

		public int GetSummaryMaxLength(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings)
		{
			// First authorize this action
			//MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, moduleLevelId,
			//                   GetModulesForUser(userInfo, portalSettings, ProviderKey));			
			
			int maxLength = 1024;
			int tabId = GetTabIDByPortalID(portalSettings.PortalId.ToString());

			// Check to see what the SummaryMaxLength is from the database
			string sSQL =
				"SELECT [Key], [Value] FROM {databaseOwner}{objectQualifier}Blog_Settings (nolock) WHERE PortalID = "
				+ portalSettings.PortalId.ToString() + " AND TabID = " + tabId.ToString();
            using (IDataReader dr =
                (IDataReader)DataProvider.Instance().ExecuteSQL(sSQL))
            {
                while (dr.Read())
                {
                    if (dr.GetString(dr.GetOrdinal("Key")) == "SummaryMaxLength")
                        maxLength =
                            Convert.ToInt32(dr.GetString(dr.GetOrdinal("Value")));
                }
            }

			return maxLength;
		}

		public string GetModuleIdFromItemId(string itemId, int portalId)
		{
			EntryInfo objEntry = GetEntry(itemId, portalId);

			return objEntry.BlogID.ToString();
		}

		#endregion

		# region Private Procedures Specific to Blog Module


		private string GetBlogIdFromEntry(EntryInfo entryInfo)
		{
			string blogId;
			// Make sure we have the parent blog
			BlogController bc = new BlogController();
			BlogInfo blogInfo = bc.GetBlog(entryInfo.BlogID);
			if (blogInfo.ParentBlogID != -1)
			{
				blogId = blogInfo.ParentBlogID.ToString();
			}
			else
			{
				blogId = blogInfo.BlogID.ToString();
			}
			return blogId;
		}

		private EntryInfo GetEntry(string itemId, int portalId)
		{
			EntryController objEntryController = new EntryController();
			// Need to use reflection to get the right procedure since 
			// the blog module authors changed the signature in
			// release 03.04.00
			Type BlogType = objEntryController.GetType();
			MethodInfo miGetEntry = BlogType.GetMethod("GetEntry");
			ParameterInfo[] piParameters = miGetEntry.GetParameters();
			EntryInfo objEntry;
			if (piParameters.Length == 1)
			{
				object[] methodParams = new object[1];
				methodParams.SetValue(Convert.ToInt32(itemId), 0);
				objEntry = (EntryInfo)miGetEntry.Invoke(objEntryController, methodParams);
			}
			else
			{
				object[] methodParams = new object[2];
				methodParams.SetValue(Convert.ToInt32(itemId), 0);
				methodParams.SetValue(portalId, 1);
				objEntry = (EntryInfo)miGetEntry.Invoke(objEntryController, methodParams);
			}
			return objEntry;
		}

		private int GetTimeZoneOffset(int blogId)
		{
			BlogController blogController = new BlogController();
			BlogInfo blog = blogController.GetBlog(blogId);
			return blog.TimeZone;
		}

		private int GetTabIDByPortalID(string PortalID)
		{
			int blogTabId = 0;
			// This is safe from SQL Injection because PortalID is retrieved in code and 
			// not passed by the user.
			string SQL =
				"SELECT Min(tm.TabID) as TabID FROM {databaseOwner}{objectQualifier}Tabs t (nolock) " +
				"	 JOIN {databaseOwner}{objectQualifier}TabModules tm (nolock) ON tm.TabId = t.TabId " +
				"    JOIN {databaseOwner}{objectQualifier}Modules m (nolock) on m.ModuleID = tm.ModuleID " +
				"    JOIN {databaseOwner}{objectQualifier}ModuleDefinitions md (nolock) on md.ModuleDefID = m.ModuleDefID " +
				"    JOIN {databaseOwner}{objectQualifier}DesktopModules dm (nolock) on dm.DesktopModuleID = md.DesktopModuleID " +
				"WHERE md.FriendlyName = 'View_Blog' " +
				"    AND m.PortalID = " + PortalID.Replace("'", "''") +
				"	 AND m.IsDeleted = 0 " + 
				"	 AND t.IsDeleted = 0 AND tm.IsDeleted = 0 ";

            using (IDataReader dr = DataProvider.Instance().ExecuteSQL(SQL))
            {
                if (dr.Read())
                {
                    blogTabId = dr.GetInt32(dr.GetOrdinal("TabID"));
                }
            }
			return blogTabId;
		}

		private void ExtractSummaryFromExtendedContent(ref Item item)
		{
			if (!string.IsNullOrEmpty(item.ExtendedContent))
			{
				// Extract the summary from the excerpt
				item.Summary = item.Content;
				item.Content = item.ExtendedContent;
			}
		}

		private void FindAndPlaceSummary(ref Item item)
		{

			if (MetaPostServices.ContainsHtml(item.Summary)) //!string.IsNullOrEmpty(item.Summary)
			{
				// We have HTML, so put the HTML in the content above a <!--more--> tag
				item.Content = item.Summary + "<!--more-->" + item.Content;
				item.Summary = string.Empty;
			}

		}

		private string SetEntryPermalink(int blogTabId, int portalId,  EntryInfo objEntry)
		{
			// Use reflection to see which version we're using.  If we're using 3.5 or higher, 
			// we'll be able to set the permalink to the new SEO friendly format.
			string permalink = string.Empty;

			MethodInfo mi =
				typeof (Utility).GetMethod("BlogNavigateURL",
				                           new Type[] {typeof (int), typeof (EntryInfo), typeof (bool)});
			if (mi == null)
			{
				mi =
				   typeof(Utility).GetMethod("BlogNavigateURL",
											  new Type[] { typeof(int), typeof(int), typeof(EntryInfo), typeof(bool) });
			}
			if (mi != null)
			{
				if (mi.GetParameters().Length == 3)
				{
					permalink = (string)mi.Invoke(null, new object[] { blogTabId, objEntry, true });	
				}
				else
				{
					permalink = (string)mi.Invoke(null, new object[] { blogTabId, portalId, objEntry, true });	
				}
			}
			else
			{
				Globals.NavigateURL(blogTabId, null, new string[] { "EntryID=" + objEntry.EntryID.ToString() });
			}

			return permalink;
		}

		# endregion

		#endregion

	}
}